#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__

#include <iostream>
#include <string>
#include <cstring>
#include <memory>

#include <sys/socket.h> // socket
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h>	// htons

#include "Log.hpp"
#include "Comm.hpp"



using namespace LogModule;
const static int gsockfd = -1;
const static std::string gdefaultip = "127.0.0.1"; // 表示本地主机
const static uint16_t gdefaultport = 8080;		   // 设置端口号

class UdpServer
{
public:
	UdpServer(const std::string &ip = gdefaultip, const uint16_t port = gdefaultport)
		: _ip(ip), _port(port), _sockfd(gsockfd), _isrunning(false)
	{
	}
	void InitServer()
	{
		// 创建套接字
		_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
		if (_sockfd < 0)
		{
			LOG(LogLevel::FATAL) << "socket: " << strerror(errno);
			Die(1);
		}
		LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;
		// 2. 填充网络信息，并bind绑定
		// 2.1 有没有把socket信息，设置进入内核中？？没有，只是填充了结构体！
		struct sockaddr_in local;
		bzero(&local, sizeof(local));
		local.sin_family = AF_INET;
		local.sin_port = ::htons(_port);				  // 本地发送给网络 是要把端口哈发出去，要把小段变大端
		local.sin_addr.s_addr = ::inet_addr(_ip.c_str()); // 1. string ip->4bytes 2. network order  ip要转换成四字节，并发到网络要转换大小端
		// bind 设置到内核
		int n = bind(_sockfd, CONV(&local), sizeof(local));
		if (n < 0)
		{
			LOG(LogLevel::FATAL) << "bind: " << strerror(errno);
			Die(2);
		}
		LOG(LogLevel::INFO) << "bind success";
	}
	void Start()
	{
		_isrunning = true;
		while (true)
		{
			char inbuffer[1024];
			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);
			ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0,CONV(&peer),&len);
			if(n > 0)
			{
				inbuffer[n] = 0;
				LOG(LogLevel::DEBUG) << "Client say@: " << inbuffer;

				std::string echo_string = "echo# ";
				echo_string += inbuffer;
				::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),len);
			}
		}
	}
	~UdpServer()
	{
	}

private:
	int _sockfd;
	uint16_t _port;	 // 服务器未来的端口号
	std::string _ip; // 服务器所对应的IP
	bool _isrunning; // 服务器的运行状态
};

#endif